/**************************************************************************
 * @name: None
 * @brief: None
 * @date: 2025/01/08
 * @author: boluzs@163.com
 * @note: None
 * @version: 1.0.0
 * @Copyright (c) 2025 by boluzs@163.com, All Rights Reserved.
 **************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>

#define LCD_DEV "/dev/fb0"
#define LCD_WIDTH 800
#define LCD_HEIGHT 480

/* 打开文件 */
int open_file(const char *path_name, int flag)
{
    // 打开lcd文件
    int lcd = open(path_name, flag);
    printf("lcd = %d\n", lcd);
    if (lcd == -1)
    {
        printf("打开文件失败\n");
        return -1;
    }
    else
    {
        printf("打开文件成功\n");
    }
    return lcd;
}

/* 写入文件 */
ssize_t write_file(int lcd, void *argb, int size)
{
    ssize_t write_res = write(lcd, argb, size);
    if (write_res == -1)
    {
        printf("写入颜色失败: ret = %ld\n", write_res);
    }
    else
    {
        printf("写入颜色成功: ret = %ld\n", write_res);
    }
    return write_res;
}

/* 关闭文件 */
int close_file(int lcd)
{
    // 关闭文件
    int ret = close(lcd);
    if (ret == -1)
    {
        printf("关闭文件失败: ret = %d\n", ret);
    }
    else if (ret == 0)
    {
        printf("关闭文件成功: ret = %d\n", ret);
    }
    return ret;
}

int main(int argc, char const *argv[])
{
    // 目标：这份代码要能够显示任意大小的图片
    // 1.判断参数是否有误
    if (argc != 2)
    {
        fprintf(stderr, "open error,errno = %d,%s\n", errno, strerror(errno));
        exit(-1);
    }
    // 第一步:打开 fb0 和 图片 文件
    int lcd = open_file(LCD_DEV, O_RDWR);
    int bmp = open_file(argv[1], O_RDONLY);
    // 第二步：申请内存映射空间
    int *mmap_star = (int *)mmap(NULL, LCD_WIDTH * LCD_HEIGHT * 4, PROT_WRITE | PROT_READ, MAP_SHARED,
                                 lcd, 0);
    if (mmap_star == (int *)-1)
    {
        printf("内存映射失败！\n");
        return -1;
    }
    else
    {
        printf("内存映射成功！\n");
    }
    // 第二步：通过图片开头的54个字节，来获取图片的宽和图片的高，跳转18个字节
    int width, height;
    lseek(bmp, 18, SEEK_SET);
    read(bmp, &width, 4);
    read(bmp, &height, 4);
    printf("width =%d, height = %d\n", width, height);
    // 第三步：通过获取到图片宽度，计算系统到底补了多少个字节数
    int last_byte = (width * 3 % 4) ? 4 - (width * 3 % 4) : 0;
    // 第四步：偏移图片开头的54个字节，然后去读取像素点，注意，要一行一行的读，读完每一行的时候，要去跳过系统补齐的字节数
    lseek(bmp, 54, SEEK_SET);
    printf("宽度为%d,余数是%d,last_byte = %d\n", width, (width % 4), last_byte);
    char buf[width * height * 3];
    for (int i = 0; i < height; i++)
    {
        ssize_t image_ret = read(bmp, buf + (i * width * 3), width * 3);
        lseek(bmp, last_byte, SEEK_CUR);
    }
    // 第五步：改造像素点
    int argb[LCD_WIDTH * LCD_HEIGHT];
    for (int y = 0; y < LCD_HEIGHT; y++)
    {
        for (int x = 0; x < LCD_WIDTH; x++)
        {
            if (x >= 0 && x < width && y >= 0 && y < height)
            {

                // 使图片从下往上画
                // argb[LCD_WIDTH * (LCD_HEIGHT - y - 1) + x] = buf[(width * y + x) * 3] + ((buf[(width * y + x) * 3 + 1]) << 8) + ((buf[(width * y + x) * 3 + 2]) << 16);
                *(mmap_star + (LCD_WIDTH * (LCD_HEIGHT - y - 1) + x)) = buf[(width * y + x) * 3] + ((buf[(width * y + x) * 3 + 1]) << 8) + ((buf[(width * y + x) * 3 + 2]) << 16);
            }
            else
            {
                // argb[LCD_WIDTH * (LCD_HEIGHT - y - 1) + x] = 0x00ffffff;
                *(mmap_star + (LCD_WIDTH * (LCD_HEIGHT - y - 1) + x)) = 0x00ffffff;
            }
        }
    }

    // 第六步：将改造好的像素点写入到fb0里面去
    // write_file(lcd, argb, LCD_WIDTH * LCD_HEIGHT * 4);

    // 最后一步：关闭两个文件
    close_file(lcd);
    close_file(bmp);
    munmap(mmap_star, LCD_WIDTH * LCD_HEIGHT * 4);
    return 0;
}